package org.zoomdev.zoom.aop.factory.impl;

import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.utils.Classes;

import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public final class AsmClassloader {
    private static final boolean useParentClassLoader = Classes.isJava8();
    private final static Method defineClassMethod;
    private static final MyClassLoader classLoader2 = new MyClassLoader();
    private static final ClassLoader systemClassLoader = AsmClassloader.class.getClassLoader();

    static {
        if(useParentClassLoader){
            try {
                defineClassMethod = ClassLoader.class.getDeclaredMethod(
                        "defineClass",
                        String.class, byte[].class, int.class, int.class, ProtectionDomain.class
                );
                defineClassMethod.setAccessible(true); // 突破访问限制
            } catch (NoSuchMethodException e) {
                throw new ZoomException(e);
            }
        }else{
            defineClassMethod = null;
        }


    }

    public static Class<?> defineClass(String className, byte[] bytecode) {
        if (useParentClassLoader) {
            try {
                return defineClass(systemClassLoader, className, bytecode);
            } catch (Exception e) {
                throw new ZoomException("无法定义类: " + className, e);
            }
        }
        try {
            return classLoader2.define(className, bytecode);
        } catch (Exception e) {
            throw new ZoomException("无法定义类: " + className, e);
        }
    }

    public static Class<?> defineClass(ClassLoader loader, String className, byte[] classBytes) throws Exception {

        // 2. 调用 defineClass 加载字节码
        // 参数说明: name, byte[], offset, length, protectionDomain
        Class<?> clazz = (Class<?>) defineClassMethod.invoke(
                loader,
                className,
                classBytes,
                0,
                classBytes.length,
                null // protectionDomain 通常为 null
        );

        return clazz;
    }

    public static ClassLoader get() {
        if (useParentClassLoader) {
            return systemClassLoader;
        }
        return classLoader2;
    }

    protected static class MyClassLoader extends ClassLoader {

        Class<?> define(String name, byte[] bytes) {
            return defineClass(name, bytes, 0, bytes.length);
        }
    }

}
